package it.antonio.ilpizze.ioc.builders;

import it.antonio.ilpizze.ioc.Builder;
import it.antonio.ilpizze.ioc.Decoration;
import it.antonio.ilpizze.ioc.Dependencies;
import it.antonio.ilpizze.ioc.InjectionMonitor;
import it.antonio.ilpizze.ioc.MapDef;
import it.antonio.ilpizze.ioc.Scope;

import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

public class MapBuilder implements Builder<Map<?,?>> {

	private Object key;
	private MapDef<? extends Object,? extends Object> config;
	
	private Scope<Map<? super Object, ? super Object>> scope;
	private List<Decoration<Map<?  super Object, ?  super Object>>> decorations = new ArrayList<Decoration<Map<? super Object,? super Object>>>();
	private List<InjectionMonitor> monitors = new ArrayList<InjectionMonitor>();
	
	public MapBuilder(Object key, MapDef<? extends Object,? extends Object> config, Scope<Map<? super Object, ? super Object>> scope, List<Decoration<Map<? super Object, ? super Object>>> decorations, List<InjectionMonitor> monitors) {
		this.key = key;
		this.config = config;
		this.scope = scope;
		this.decorations = decorations;
		this.monitors = monitors;
	}

	@Override
	public Object getKey() {
		return key;
	}

	@SuppressWarnings("unchecked")
	@Override
	public Class<? extends Map<?,?>> getType() {
		return (Class<? extends Map<?, ?>>) Map.class;
	}

	@Override
	public Map<?, ?> build(Dependencies dependencies) {
		
		for(InjectionMonitor monitor: monitors){
			monitor.before(getKey(),getType());
		}
		
		Map<? super Object, ? super Object> map = scope.get();
		if(map != null){
			return map;
		} else {
			map = new LinkedHashMap<Object, Object>();
			for(Entry<? extends Object, Object> entry: config){
				Object instance = dependencies.get(entry.getValue()); 
				map.put(entry.getKey(), instance);
			}
			for (Decoration<Map<? super Object, ? super Object>> decoration : decorations) {
				decoration.decorate(map, dependencies);
			}
			scope.set(map);
		}
		for(InjectionMonitor monitor: monitors){
			monitor.after(getKey(),getType());
		}
		
		return map;
	}

}
